<template>
  <div class="upload-container">
    <el-upload
      v-model:file-list="fileList"
      action=""
      :limit="setting.limit"
      :accept="acceptTypes"
      :on-exceed="handleExceed"
      :on-error="handleError"
      :before-upload="beforeUpload"
      :http-request="handleHttpRequest"
      :on-preview="handlePreview"
      :disabled="props.disabled"
    >
      <el-button type="primary" size="large">
        <el-icon><Upload /></el-icon>上传文件
      </el-button>

      <template #tip>
        <div class="upload-tip">
          <p>支持上传的文件类型：{{ getAcceptTypeDesc }}</p>
          <p>文件大小限制：{{ setting.fileSize }}MB以内</p>
          <p v-if="setting.limit > 1">最多可上传 {{ setting.limit }} 个文件</p>
        </div>
      </template>
    </el-upload>
    <el-dialog v-model="previewVisible">
      <img w-full :src="previewUrl" alt="Preview Image" />
    </el-dialog>
  </div>
</template>

<script setup>
import { computed, ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Upload } from '@element-plus/icons-vue'
import { singleUpload, splitUpload } from '@/utils/upload'

const props = defineProps({
  modelValue: {
    type: Array,
    default: () => []
  },
  setting: {
    type: Object,
    default: () => {}
  },
  formRules: {
    type: Object,
    default: () => ({})
  },
  disabled: {
    type: Boolean,
    default: false
  }
})

const emits = defineEmits(['update:modelValue', 'update:formRules'])

const singleActionUrl = ref(process.env.VUE_APP_SERVICE_BASE + '/file/sysFile/upload')
const splitActionUrl = ref(process.env.VUE_APP_SERVICE_BASE + '/file/sysFile/uploadChunk')
const fileList = computed({
  get: () => {
    return (props.modelValue || []).map(item => ({
      name: item.fileName,
      url: item.url
    }))
  },
  set: (val) => {
    if (!val) return
    if (JSON.stringify(val) === JSON.stringify(fileList.value)) {
      return
    }
    const newVal = val.map(item => ({
      fileName: item.name,
      url: item.url
    }))
    emits('update:modelValue', newVal)
  }
})

// 处理 formRules
const formRules = computed({
  get: () => props.formRules,
  set: (val) => {
    emits('update:formRules', val)
  }
})

// 计算接受的文件类型
const acceptTypes = computed(() => {
  const types = props.setting.accept
    .map((type) =>
      type
        .split('|')
        .map((t) => `.${t}`)
        .join(',')
    )
    .join(',')
  return types
})

// 获取文件类型描述
const getAcceptTypeDesc = computed(() => {
  return props.setting.accept.join('/')
})

// 处理超出文件数量限制
const handleExceed = () => {
  ElMessage.warning(`最多只能上传 ${props.setting.limit} 个文件`)
}

// 上传前验证
const beforeUpload = (file) => {
  // 验证文件大小
  const isLtSize = file.size / 1024 / 1024 < props.setting.fileSize
  if (!isLtSize) {
    ElMessage.error(`文件大小不能超过 ${props.setting.fileSize}MB!`)
    return false
  }

  // 验证文件类型
  const fileName = file.name
  const fileExt = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase()
  const isValidType = props.setting.accept.some((types) => types.split('|').includes(fileExt))

  if (!isValidType) {
    ElMessage.error('文件类型不正确!')
    return false
  }

  return true
}

// 处理上传请求
const handleHttpRequest = async (option) => {
  if (option.file.size > 10 * 1024 * 1024) {
    let res = await splitUpload(option.file, splitActionUrl.value, option.onProgress, 10 * 1024 * 1024)
    handleSuccess(res.data)
  } else {
    let data = new FormData()
    data.append('file', option.file)
    data.append('path', 'public')
    let res = await singleUpload(data, singleActionUrl.value, option.onProgress)
    handleSuccess(res.data.url)
  }
}

// 上传成功处理
const handleSuccess = (url) => {
  fileList.value[fileList.value.length - 1].url = url
  emits('update:modelValue', fileList.value.map(item => ({
    fileName: item.name,
    url: item.url
  })))
}

// 上传失败处理
const handleError = (uid) => {
  ElMessage.error('上传失败')
  const index = fileList.value.findIndex(file => file.uid === uid)
  fileList.value.splice(index, 1)
}

// 预览文件，如果是图片类，则弹窗预览，否则进行下载确认
const previewVisible = ref(false)
const previewUrl = ref('')
const handlePreview = (data) => {
  console.log(data, 666)
  if (data.name.endsWith('.jpg') || data.name.endsWith('.png') || data.name.endsWith('.jpeg')) {
    previewUrl.value = data.url
    previewVisible.value = true
  } else {
    window.open(data.url)
  }
}

// 生成校验规则
const generateRules = () => {
  const rules = []

  if (props.setting.require) {
    rules.push({
      required: true,
      message: '此题为必填题',
      trigger: 'change',
      validator: (rule, value, callback) => {
        if (!fileList.value || fileList.value.length === 0) {
          callback(new Error('请上传文件'))
          return
        }
        callback()
      }
    })
  }

  formRules.value[props.setting.formItemId] = rules
}

// 组件挂载时生成验证规则
onMounted(() => {
  generateRules()
})
</script>

<style lang="scss" scoped>
.upload-container {
  width: 100%;

  :deep(.el-upload) {
    width: 100%;
    justify-content: flex-start;
    .el-button {
      margin-bottom: 8px;
    }
  }

  .upload-tip {
    color: #909399;
    font-size: 12px;
    line-height: 1.5;
  }
}

:deep(.el-dialog__body) {
  text-align: center;

  img {
    max-width: 100%;
    max-height: 70vh;
  }
}
</style>
